#include "bindinggenerator_mt.h"

#include <arc.h>
#include <bindinggenerator.h>
#include <bool.h>
#include <boolproxy.h>
#include <bstring.h>
#include <bstringproxy.h>
#include <dynamictypemanager.h>
#include <expression.h>
#include <expressionengine.h>
#include <expressionvalidator.h>
#include <multiset.h>
#include <multisetproxy.h>
#include <number.h>
#include <numberproxy.h>
#include <place.h>
#include <timedmultisetproxy.h>
#include <transition.h>
#include <typefactory.h>
#include <wfnet.h>

#include <QtTest/QTest>

using namespace core;
using namespace wfnet;
using namespace moduletest;

void BindingGenerator_mt::initTestCase()
{
    Bool boolean;
    BasicString string;
    Number number;

    m_net = new WfNet(this);

    DynamicTypeManager::getInstance()->registerColorSet(
        "Booleans", boolean, m_net);
    DynamicTypeManager::getInstance()->registerColorSet(
        "Strings", string, m_net);
    DynamicTypeManager::getInstance()->registerColorSet(
        "Numbers", number, m_net);
    DynamicTypeManager::getInstance()->registerColorSet(
        "NumbersTimed", number, m_net, true);

    DynamicTypeManager::getInstance()->registerVariable(
        "b", "Booleans", m_net);

    DynamicTypeManager::getInstance()->registerVariable(
        "s", "Strings", m_net);
    DynamicTypeManager::getInstance()->registerVariable(
        "t", "Strings", m_net);

    DynamicTypeManager::getInstance()->registerVariable(
        "n", "Numbers", m_net);
    DynamicTypeManager::getInstance()->registerVariable(
        "k", "Numbers", m_net);

    DynamicTypeManager::getInstance()->registerVariable(
        "nTimed", "NumbersTimed", m_net);
    DynamicTypeManager::getInstance()->registerVariable(
        "kTimed", "NumbersTimed", m_net);

    ExpressionValidator::getInstance()->registerKeywordMasks(
        TypeFactory::getInstance()->registeredKeywordMasks());

    ExpressionEngine::getInstance()->registerTypeProxy(Bool::NAME,
        BoolProxy::NAME);
    ExpressionEngine::getInstance()->registerTypeProxy(BasicString::NAME,
        BasicStringProxy::NAME);
    ExpressionEngine::getInstance()->registerTypeProxy(Number::NAME,
        NumberProxy::NAME);
    ExpressionEngine::getInstance()->registerTypeProxy(Multiset::NAME,
        MultisetProxy::NAME);
    ExpressionEngine::getInstance()->registerTypeProxy(TimedMultiset::NAME,
        TimedMultisetProxy::NAME);
}

void BindingGenerator_mt::bind()
{
    QFETCH(bool, value);
    QFETCH(bool, result);

    QCOMPARE(value, result);
}

void BindingGenerator_mt::bind_data()
{
    ColorSet numbers = *(dynamic_cast<const ColorSet*>(
        TypeFactory::getInstance()->getTypeInstance("Numbers")));

    ColorSet numbersTimed = *(dynamic_cast<const ColorSet*>(
        TypeFactory::getInstance()->getTypeInstance("NumbersTimed")));

    QSet<const Arc* > toBeCheckedArcs;

    // first case - SIMPLE_VAL_ONLY
    IPlace* p1_1 = m_net->addPlace(&numbers, "p1_1");
    IPlace* p2_1 = m_net->addPlace(&numbers, "p2_1");

    Transition* t1_1 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_1"));

    IExpression* e11_11 = m_net->addExpression("{1*2}", "Numbers");
    IExpression* e21_11 = m_net->addExpression("{1*1}", "Numbers");

    Arc* a11_11 = dynamic_cast<Arc* >(m_net->connect(p1_1, t1_1));
    a11_11->setExpression(e11_11);
    Arc* a21_11 = dynamic_cast<Arc* >(m_net->connect(p2_1, t1_1));
    a21_11->setExpression(e21_11);

    QSet<const Arc* > case1;
    case1 << a11_11 << a21_11;

    Multiset ms("Numbers");

    // second case - SIMPLE_VAR_ONLY
    IPlace* p1_2 = m_net->addPlace(&numbers, "p1_2");
    ms = Multiset("Numbers", "{1*2 + 1*3}");
    p1_2->putTokens(&ms);
    IPlace* p2_2 = m_net->addPlace(&numbers, "p2_2");
    ms = Multiset("Numbers", "{1*1}");
    p2_2->putTokens(&ms);

    Transition* t1_2 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_2"));

    IExpression* e12_12 = m_net->addExpression("n", "Numbers");
    IExpression* e22_12 = m_net->addExpression("k", "Numbers");

    Arc* a12_12 = dynamic_cast<Arc* >(m_net->connect(p1_2, t1_2));
    a12_12->setExpression(e12_12);
    Arc* a22_12 = dynamic_cast<Arc* >(m_net->connect(p2_2, t1_2));
    a22_12->setExpression(e22_12);

    QSet<const Arc* > case2;
    case2 << a12_12 << a22_12;

    // third case - SIMPLE_MUL_VAR
    IPlace* p1_3 = m_net->addPlace(&numbers, "p1_3");
    ms = Multiset("Numbers", "{1*2 + 1*3}");
    p1_3->putTokens(&ms);
    IPlace* p2_3 = m_net->addPlace(&numbers, "p2_3");
    ms = Multiset("Numbers", "{1*2}");
    p2_3->putTokens(&ms);
    IPlace* p3_3 = m_net->addPlace(&numbers, "p3_3");
    ms = Multiset("Numbers", "{1*1}");
    p3_3->putTokens(&ms);

    Transition* t1_3 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_3"));

    IExpression* e13_13 = m_net->addExpression("n", "Numbers");
    IExpression* e23_13 = m_net->addExpression("n", "Numbers");
    IExpression* e33_13 = m_net->addExpression("k", "Numbers");

    Arc* a13_13 = dynamic_cast<Arc* >(m_net->connect(p1_3, t1_3));
    a13_13->setExpression(e13_13);
    Arc* a23_13 = dynamic_cast<Arc* >(m_net->connect(p2_3, t1_3));
    a23_13->setExpression(e23_13);
    Arc* a33_13 = dynamic_cast<Arc* >(m_net->connect(p3_3, t1_3));
    a33_13->setExpression(e33_13);

    QSet<const Arc* > case3;
    case3 << a13_13 << a23_13 << a33_13;

    // fourth case - COMPLEX_VAR_VAL
    IPlace* p1_4 = m_net->addPlace(&numbers, "p1_4");
    ms = Multiset("Numbers", "{1*4 + 1*5 + 1*6}");
    p1_4->putTokens(&ms);
    IPlace* p2_4 = m_net->addPlace(&numbers, "p2_4");
    ms = Multiset("Numbers", "{1*2 + 1*3 + 1*4 + 1*5}");
    p2_4->putTokens(&ms);
    IPlace* p3_4 = m_net->addPlace(&numbers, "p3_4");
    ms = Multiset("Numbers", "{1*3 + 1*4 + 1*5}");
    p3_4->putTokens(&ms);
    IPlace* p4_4 = m_net->addPlace(&numbers, "p4_4");
    ms = Multiset("Numbers", "{1*3 + 1*4 + 1*5}");
    p4_4->putTokens(&ms);

    Transition* t1_4 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_4"));

    IExpression* e14_14 = m_net->addExpression("n + 1", "Numbers");
    IExpression* e24_14 = m_net->addExpression("n", "Numbers");
    IExpression* e34_14 = m_net->addExpression("n", "Numbers");
    IExpression* e44_14 = m_net->addExpression("k", "Numbers");

    Arc* a14_14 = dynamic_cast<Arc* >(m_net->connect(p1_4, t1_4));
    a14_14->setExpression(e14_14);
    Arc* a24_14 = dynamic_cast<Arc* >(m_net->connect(p2_4, t1_4));
    a24_14->setExpression(e24_14);
    Arc* a34_14 = dynamic_cast<Arc* >(m_net->connect(p3_4, t1_4));
    a34_14->setExpression(e34_14);
    Arc* a44_14 = dynamic_cast<Arc* >(m_net->connect(p4_4, t1_4));
    a44_14->setExpression(e44_14);

    QSet<const Arc* > case4;
    case4 << a14_14 << a24_14 << a34_14 << a44_14;

    TimedMultiset tms;

    // fifth case - timed SIMPLE_VAR_ONLY
    IPlace* p1_5 = m_net->addPlace(&numbersTimed, "p1_5");
    tms = TimedMultiset("NumbersTimed", "{1*2@10 + 1*3@5}");
    p1_5->putTokens(&tms);
    IPlace* p2_5 = m_net->addPlace(&numbersTimed, "p2_5");
    tms = TimedMultiset("NumbersTimed", "{1*1@0 + 1*1@1}");
    p2_5->putTokens(&tms);

    Transition* t1_5 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_5"));

    IExpression* e15_15 = m_net->addExpression("nTimed", "NumbersTimed",
        nid(), true);
    IExpression* e25_15 = m_net->addExpression("kTimed", "NumbersTimed",
        nid(), true);

    Arc* a15_15 = dynamic_cast<Arc* >(m_net->connect(p1_5, t1_5));
    a15_15->setExpression(e15_15);
    Arc* a25_15 = dynamic_cast<Arc* >(m_net->connect(p2_5, t1_5));
    a25_15->setExpression(e25_15);

    QSet<const Arc* > case5;
    case5 << a15_15 << a25_15;

    // sixth case - timed SIMPLE_MUL_VAR
    IPlace* p1_6 = m_net->addPlace(&numbersTimed, "p1_6");
    tms = TimedMultiset("NumbersTimed", "{1*2@3 + 1*3@0}");
    p1_6->putTokens(&tms);
    IPlace* p2_6 = m_net->addPlace(&numbersTimed, "p2_6");
    tms = TimedMultiset("NumbersTimed", "{1*2@4}");
    p2_6->putTokens(&tms);
    IPlace* p3_6 = m_net->addPlace(&numbersTimed, "p6_6");
    tms = TimedMultiset("NumbersTimed", "{1*1@2}");
    p3_6->putTokens(&tms);

    Transition* t1_6 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_6"));

    IExpression* e16_16 = m_net->addExpression("nTimed", "NumbersTimed",
        nid(), true);
    IExpression* e26_16 = m_net->addExpression("nTimed", "NumbersTimed",
        nid(), true);
    IExpression* e36_16 = m_net->addExpression("kTimed", "NumbersTimed",
        nid(), true);

    Arc* a16_16 = dynamic_cast<Arc* >(m_net->connect(p1_6, t1_6));
    a16_16->setExpression(e16_16);
    Arc* a26_16 = dynamic_cast<Arc* >(m_net->connect(p2_6, t1_6));
    a26_16->setExpression(e26_16);
    Arc* a36_16 = dynamic_cast<Arc* >(m_net->connect(p3_6, t1_6));
    a36_16->setExpression(e36_16);

    QSet<const Arc* > case6;
    case6 << a16_16 << a26_16 << a36_16;

    // seventh case - COMPLEX_VAR_VAL
    IPlace* p1_7 = m_net->addPlace(&numbersTimed, "p1_7");
    tms = TimedMultiset("NumbersTimed", "{1*4@0 + 1*5@2 + 1*6@5}");
    p1_7->putTokens(&tms);
    IPlace* p2_7 = m_net->addPlace(&numbersTimed, "p2_7");
    tms = TimedMultiset("NumbersTimed", "{1*2@0 + 1*3@2 + 1*4@5 + 1*5@10}");
    p2_7->putTokens(&tms);
    IPlace* p3_7 = m_net->addPlace(&numbersTimed, "p3_7");
    tms = TimedMultiset("NumbersTimed", "{1*3@1 + 1*4@2 + 1*5@8}");
    p3_7->putTokens(&tms);
    IPlace* p4_7 = m_net->addPlace(&numbersTimed, "p4_7");
    tms = TimedMultiset("NumbersTimed", "{1*3@10 + 1*4@5 + 1*5@2}");
    p4_7->putTokens(&tms);

    Transition* t1_7 = dynamic_cast<Transition* >(
        m_net->addTransition("t1_7"));

    IExpression* e17_17 = m_net->addExpression("nTimed + 1", "NumbersTimed",
        nid(), true);
    IExpression* e27_17 = m_net->addExpression("nTimed", "NumbersTimed",
        nid(), true);
    IExpression* e37_17 = m_net->addExpression("nTimed", "NumbersTimed",
        nid(), true);
    IExpression* e47_17 = m_net->addExpression("kTimed", "NumbersTimed",
        nid(), true);

    Arc* a17_17 = dynamic_cast<Arc* >(m_net->connect(p1_7, t1_7));
    a17_17->setExpression(e17_17);
    Arc* a27_17 = dynamic_cast<Arc* >(m_net->connect(p2_7, t1_7));
    a27_17->setExpression(e27_17);
    Arc* a37_17 = dynamic_cast<Arc* >(m_net->connect(p3_7, t1_7));
    a37_17->setExpression(e37_17);
    Arc* a47_17 = dynamic_cast<Arc* >(m_net->connect(p4_7, t1_7));
    a47_17->setExpression(e47_17);

    QSet<const Arc* > case7;
    case7 << a17_17 << a27_17 << a37_17 << a47_17;

    BindingGenerator* generator = BindingGenerator::getInstance();
    Binding binding;

    QTest::addColumn<bool>("value");
    QTest::addColumn<bool>("result");

    BasicTime pGC;
    BasicTime epGC(0);

    // bindable
    QTest::newRow("case1") << (generator->bind(binding, 0, pGC, case1,
        QSet<const Arc*>(), t1_1, toBeCheckedArcs) && epGC == pGC)  << true;

    binding.clear();
    toBeCheckedArcs.clear();

    QTest::newRow("case2") << (generator->bind(binding, 0, pGC, case2,
        QSet<const Arc*>(), t1_2, toBeCheckedArcs) && epGC == pGC) << true;

    binding.clear();
    toBeCheckedArcs.clear();

    QTest::newRow("case3") << (generator->bind(binding, 0, pGC, case3,
        QSet<const Arc*>(), t1_3, toBeCheckedArcs) && epGC == pGC) << true;

    binding.clear();
    toBeCheckedArcs.clear();

    QTest::newRow("case4") << (generator->bind(binding, 0, pGC, case4,
        QSet<const Arc*>(), t1_4, toBeCheckedArcs) && epGC == pGC) << true;

    binding.clear();
    toBeCheckedArcs.clear();

    epGC.setTime(5);

    QTest::newRow("case5") << (generator->bind(binding, 2, pGC, case5,
        QSet<const Arc*>(), t1_5, toBeCheckedArcs) && epGC == pGC) << true;

    binding.clear();
    toBeCheckedArcs.clear();

    epGC.setTime(4);

    QTest::newRow("case6") << (generator->bind(binding, 0, pGC, case6,
        QSet<const Arc*>(), t1_6, toBeCheckedArcs) && epGC == pGC) << true;

    binding.clear();
    toBeCheckedArcs.clear();

    epGC.setTime(2);

    QTest::newRow("case7") << (generator->bind(binding, 1, pGC, case7,
        QSet<const Arc*>(), t1_7, toBeCheckedArcs) && epGC == pGC) << true;

    // not bindable
    ms = Multiset("Numbers", "{1*1}");
    p2_2->withdrawTokens(&ms);

    binding.clear();
    toBeCheckedArcs.clear();

    QTest::newRow("case2 NOT BINDABLE") << !generator->bind(binding, 0,
        pGC, case2, QSet<const Arc*>(), t1_2, toBeCheckedArcs) << true;

    ms = Multiset("Numbers", "{1*2}");
    p2_3->withdrawTokens(&ms);

    binding.clear();
    toBeCheckedArcs.clear();

    QTest::newRow("case3 NOT BINDABLE") << !generator->bind(binding, 0,
        pGC, case2, QSet<const Arc*>(), t1_3, toBeCheckedArcs) << true;

    ms = Multiset("Numbers", "{1*5 + 1*6}");
    p1_4->withdrawTokens(&ms);
    ms = Multiset("Numbers", "{1*3}");
    p3_4->withdrawTokens(&ms);

    binding.clear();
    toBeCheckedArcs.clear();

    QTest::newRow("case4 NOT BINDABLE") << !generator->bind(binding, 0,
        pGC, case2, QSet<const Arc*>(), t1_4, toBeCheckedArcs) << true;
}

void BindingGenerator_mt::cleanupTestCase()
{
    DynamicTypeManager::getInstance()->unregisterColorSet("Booleans",
        m_net);
    DynamicTypeManager::getInstance()->unregisterColorSet("Strings",
        m_net);
    DynamicTypeManager::getInstance()->unregisterColorSet("Numbers",
        m_net);
    DynamicTypeManager::getInstance()->unregisterVariable("b", m_net);
    DynamicTypeManager::getInstance()->unregisterVariable("s", m_net);
    DynamicTypeManager::getInstance()->unregisterVariable("t", m_net);
    DynamicTypeManager::getInstance()->unregisterVariable("n", m_net);
    DynamicTypeManager::getInstance()->unregisterVariable("k", m_net);

    ExpressionValidator::getInstance()->clearKeywordMasks();

    ExpressionEngine::getInstance()->unregisterTypeProxy(Bool::NAME);
    ExpressionEngine::getInstance()->unregisterTypeProxy(BasicString::NAME);
    ExpressionEngine::getInstance()->unregisterTypeProxy(Number::NAME);
    ExpressionEngine::getInstance()->unregisterTypeProxy(TimedMultiset::NAME);
}
